package handle

import (
	"bytes"
	"encoding/json"
	"github.com/cavaliergopher/grab/v3"
	"github.com/nacos-group/nacos-sdk-go/common/logger"
	"html/template"
	"io/ioutil"
	"mgs/convert"
	"mgs/global"
	"mgs/oss"
	"mgs/toolkit"
	"mime/multipart"
	"net/http"
	"net/url"
	"path"
	"path/filepath"
	"strings"
	"sync"
)

// TestFile2pdf 上传测试 https://www.cnblogs.com/f-ck-need-u/p/10053124.html
func TestFile2pdf(rw http.ResponseWriter, r *http.Request) {
	t, err := template.ParseFiles("templates/upload.html")
	if err != nil {
		panic(err)
	}
	t.Execute(rw, global.CONFIG.System.ApiPrefix)
}

// File2pdf 上传文件转换PDF
func File2pdf(rw http.ResponseWriter, r *http.Request) {
	filename := r.FormValue("filename")
	r.ParseMultipartForm(100)
	files := r.MultipartForm.File["file"]
	fileSize := len(files)
	if fileSize == 0 {
		rw.Write([]byte("Not found upload file."))
		return
	}

	// 开始转换PDF
	var pdfUrls []string
	var tempPath = global.CONFIG.System.TempPath

	// 管理协程并发
	var wg sync.WaitGroup
	//遍历数组进行处理
	for i := 0; i < fileSize; i++ {
		if !toolkit.Allow(files[i].Filename) {
			rw.Write([]byte("Only office file processing is supported."))
			return
		}
		// 添加一个协程
		wg.Add(1)
		go func(i int) {
			//打开上传文件
			file, err := files[i].Open()
			if err == nil {
				// 开始转换PDF
				pdfUrl, pdfErr := convertPdf(file, files[i].Filename, tempPath)
				if pdfErr == nil {
					pdfUrls = append(pdfUrls, pdfUrl)
				}
			}
			// 当前协程执行完成
			wg.Done()
		}(i)
	}
	// 等待协程完成
	wg.Wait()

	// 多文件下载
	downloadMultipart(rw, pdfUrls, filename)
}

func convertPdf(file multipart.File, filename, tempPath string) (outFile string, err error) {
	ext := path.Ext(filename)
	var infile = path.Join(tempPath, toolkit.Uuid()) + ext
	b, err := ioutil.ReadAll(file)
	if err != nil {
		return
	}
	err = ioutil.WriteFile(infile, b, 0777)
	if err != nil {
		return
	}

	// 文件转换
	return convert.Export(ext, infile, tempPath)
}

// Key2pdf 文件key下载转PDF
func Key2pdf(rw http.ResponseWriter, r *http.Request) {
	var pdfParams PdfParams
	if r.Method == "POST" {
		// JSON 格式解析参数
		body, _ := ioutil.ReadAll(r.Body)
		json.Unmarshal(body, &pdfParams)
	} else if r.Method == "GET" {
		filename := r.FormValue("filename")
		key := r.FormValue("key")
		if len(key) > 0 {
			pdfParams = PdfParams{
				Filename: filename,
				Keys:     strings.Split(key, ","),
			}
		}
	} else {
		// 直接返回不处理
		return
	}

	if len(pdfParams.Keys) == 0 {
		rw.Write([]byte("key is required."))
		return
	}

	// 开始转换PDF
	var pdfUrls []string
	var tempPath = global.CONFIG.System.TempPath

	// 管理协程并发
	var wg sync.WaitGroup
	fileSize := len(pdfParams.Keys)
	//遍历数组进行处理
	for i := 0; i < fileSize; i++ {
		if !toolkit.Allow(pdfParams.Keys[i]) {
			rw.Write([]byte("Only office file processing is supported."))
			return
		}
		// 添加一个协程
		wg.Add(1)
		go func(i int) {
			pdfUrl, pdfErr := downloadConvertPdf(tempPath, pdfParams.Keys[i])
			if pdfErr == nil {
				pdfUrls = append(pdfUrls, pdfUrl)
			}
			// 当前协程执行完成
			wg.Done()
		}(i)
	}
	// 等待协程完成
	wg.Wait()

	// 多文件下载
	downloadMultipart(rw, pdfUrls, pdfParams.Filename)
}

func downloadConvertPdf(tempPath, key string) (pdfUrl string, err error) {
	fileExt := toolkit.KeyFileExt(key)
	infile := path.Join(tempPath, toolkit.Uuid()) + fileExt
	_, err = url.ParseRequestURI(key)
	if err == nil {
		_, err := grab.Get(infile, key)
		if err != nil {
			return pdfUrl, err
		}
	} else {
		// 根据 key 下载文件
		err = oss.NewOss().FGetObject(key, infile)
		if err != nil {
			logger.Debug("OSS FGetObject failed.", err)
			return pdfUrl, err
		}
	}
	objectDir, _ := filepath.Split(infile)

	// 文件转换
	return convert.Export(fileExt, infile, objectDir)
}

func downloadMultipart(rw http.ResponseWriter, pdfUrls []string, filename string) {
	pdfFiles := len(pdfUrls)
	if pdfFiles == 0 {
		// 有损文件
		rw.Write([]byte("File does not exist or is corrupt."))
		return
	}
	// 下载文件名
	var dn = downloadFilename(filename)
	var out []byte
	var err error
	if pdfFiles > 1 {
		// 下载多个文件打包 zip
		dn += ".zip"
		buf := new(bytes.Buffer)
		toolkit.ZipWriter(buf, pdfUrls)
		out = buf.Bytes()
	} else {
		dn += ".pdf"
		// 读取文件
		out, err = ioutil.ReadFile(pdfUrls[0])
		if err != nil {
			rw.Write([]byte("The system cannot find the file specified"))
			return
		}
	}

	// 文件下载
	download(rw, out, dn)
}

func downloadFilename(filename string) string {
	// 未指定文件名
	if len(filename) == 0 {
		filename = toolkit.Uuid()
	}
	return filename
}

func download(rw http.ResponseWriter, out []byte, filename string) {
	// 设置响应头
	outHeader := rw.Header()
	outHeader.Add("Content-Type", "application/octet-stream")
	outHeader.Add("Content-Disposition", "attachment;filename="+filename)
	// 写入到响应流中
	rw.Write(out)
}

type PdfParams struct {
	Filename string   `json:"filename"`
	Keys     []string `json:"keys"`
}
